Back To Tedon Software

Debug a PowerShell script running in PowerShell console locally or remote after it has started

Always wanted to debug a running PowerShell script in a console which looks like it's stuck on something. Now you can in PowerShell 5.0 with the following steps.

  • First find the process id of the running script in the taskmanager details tab (PID).
  • Now start PowerShell ISE and activate the console (Ctrl-D)
  • Enter Enter-PSHostProcess -id yourid
  • Enter Debug-Runspace -id 1 (most of the time it's Runspace 1)

PowerShell ISE will now load the script and show it halted at the currently executed statement. Now you can run it again after inspection or continue debugging it and/or set breakpoints.

To test this scenario create a long running script like this

Write-Host "Long running script with PID: $pid"

while ($true)
{
    sleep -Seconds 1
    Write-Host "In next cycle"
}

Start this script in a PowerShell console and start PowerShell ISE with the following commands

Enter-PSHostProcess -id 14468 # enter here your script pid
Debug-Runspace -id 1

If your script is running on another machine and PowerShell remoting is configured you can also do this with remote scripts. Before using Enter-PSHostProcess open a session with Enter-PSSession -ComputerName and follow the same steps.

If you want more details about this functionality lookup the following commands on Microsofts site.

Enter-PSHostProcess, Get-Runspace, Debug-Runspace, Enable-RunspaceDebug -BreakAll, Wait-Debugger, Enter-PSSession

The implementation of PowerShell runspaces gives us a lot of flexibility and allows for some crazy scenarios. Say I want to run a script and let it break at some point with the Wait-Debugger statement and continue debugging with PowerShell ISE. When I add the Wait-Debugger statement the PowerShell console running the script breaks at the statement in the cmd-line debugger of PowerShell. Thats not what I want, I want PowerShell ISE. You can work-around this by using a helper script which executes your script.

[CmdletBinding()]
Param(
    [alias('f')]
    [string] $aFile = "$PSScriptRoot\long-running.ps1"
)

Set-StrictMode -Version Latest
Set-Location $PSScriptRoot

echo "Debug Script"
echo ""
echo "Parameters"
echo "        File: $aFile"
echo ""
echo "Computer details"
echo "     Computer: $($env:ComputerName)"
echo "       WhoAmI: $(WhoAmI)"
echo "   PowerShell: $($psversiontable.PsVersion)"
echo "         .Net: $($psversiontable.ClrVersion)"
echo "Currentfolder: $(get-location)"
echo ""
                               
$rs = [System.Management.Automation.RunspaceMode]::NewRunspace                               
$PowerShell = [PowerShell]::Create($rs)

Write-Host "**** Script is running in debugging mode ****"
Write-Host ""
Write-Host "To start debugging execute the following commands in a PS-Console"
Write-Host ""
Write-Host "`t Enter-PSSession -ComputerName $($env:computername) -Credential 'yourcredentials'"
Write-Host "`t Enter-PSHostProcess -id $pid"
Write-Host "`t Debug-Runspace -id $($PowerShell.Runspace.Id)"
Write-Host ""
Write-Host "To end debugging, perform the 'Exit-PSHostProcess' in PS-Console after abort"

[void]$PowerShell.AddScript(". '$aFile'")
[void]$PowerShell.Invoke()

Write-Host "Debugging session ended"

Add to your long-running script the Wait-Debugger or Enable-RunspaceDebug -BreakAll

Write-Host "Long running script with PID: $pid"

while ($true)
{
    sleep -Seconds 1
    Enable-RunspaceDebug -BreakAll
    Write-Host "In next cycle"    
}

Executing the scenario mentioned at the beginning of the blog-post shows you that the script will break at the Enable-RunspaceDebug statement.